/**
 * \file viewer.h
 *
 * \brief Enthält die Klasse Viewer.
 */

#ifndef _VIEWER_H_
#define _VIEWER_H_

#include <cairo.h>
#include <gtkmm.h>

#include <gtk/gtk.h>
#include <gtk/gtkgl.h>
#include <gdk/gdkglglext.h>
#include <glibmm.h>

#include <GL/gl.h>
#include <GL/glu.h>

#include <vector>
#include "feld.h"

class Feld;

/**
* Eine Struktur um ein Punkt fuer OpenGl zu beschreiben
*/

typedef struct{
/*@{*/
   GLubyte r; /**< Rot Wert des Pixels */
   GLubyte g; /**< Gruen Wert des Pixels */
   GLubyte b; /**< Blau Wert des Pixels */
   GLubyte a; /**< Alpha Wert des Pixels */
   GLfloat  x; /**< X-Koordinate des Pixels */
   GLfloat  y; /**< Y-Koordinate des Pixels */
/*@}*/    
}Point;

class Viewer{
    
    Point* Pointarr; /**< Datenstruktur, die OpenGl benoetigt, um Feld schnell darzustellen */
    Point* Gitarr; /**<  Datenstruktur, die OpenGl benoetigt, um das Gitter schnell darzustellen*/
    Gtk::Window *window;
    GtkWidget * drawing_area_old;
    Gtk::Adjustment *adjx,*adjy,*adjzoom;
    long fieldx; /**< Breite des darzustellenden Feldes */
    long fieldy; /**< Hoehe des darzustellenden Feldes */
    Glib::Mutex *give_me_next_number_mutex;	/**< Mutex, der das Zeichnen koordiniert */	
    Glib::Mutex *draw_mutex; /**< Mutex, der Aktionsmanager warten laesst, wenn er das zu zeichnende Feld veraendern will, es aber noch nie gezeichnet wurde  */	
    int mausxold; /**< alte X-Koordinate der Maus, (um sinnloses Neuzeichnen zu vermeiden)*/
    int mausyold; /**< alte Y-Koordinate der Maus, (um sinnloses neuzeichnen zu vermeiden)*/
    int anzsave; /**< Speichert wie viele schwarze Pixel gezeichnet werden muessen*/
    Feld *  fald; /**< Pointer auf das Feld*/
   

    /**
    * @brief Berechnet eine Koordinate der Zelle, auf der der Mauszeiger sich befindet.
    * @param mausx absolute Mauskoordinate bzg Fenster (X oder Y Position)
    * @param scrx Position des Scrollbalkens (Breite oder Hoehe)
    * @param dimfield Dimension des Fenstersx (Breite oder Hoehe)
    * @param verschiebungx berechnete Verschiebung des Feldes (fuer Breite oder Hoehe)
    * @param fieldx Dimension des Feldes (Breite oder Hoehe)
    * Berechnet eine Koordinate der Zelle, worueber sich der Mauszeiger gerade befindet.
    */

    int mouse_to_field(int mausx, double scrx, int dimfield,int verschiebungx,int fieldx);
    int minab(int a, int b);
    
    /**
    * @brief Berechnet das Gitter.
    * @param aa maximum von Breite des Feldes / des Fensters
    * @param bb maximum von Hoehe des Feldes / des Fensters
    *
    * Berechnet das Gitter, das im REC-Modus zur besseren Uebersicht angezeigt wird, und speichert
    * es in einen OpenGl zulaessigen Format.
    */
    int init_gitarr(int aa, int bb);
    
    /**
    * @brief Berechnet den darzustellenden Bildschirmausschnitt.
    *
    * @param aa maximum von Breite des Feldes / des Fensters
    * @param bb maximum von Hoehe des Feldes / des Fensters
    * @param xuz berechnete X-Verschiebung des Feldes in Pixel (durch scrollen und zoomen)
    * @param yuz berechnete Y-Verschiebung des Feldes in Pixel (durch scrollen und zoomen)
    *
    * Berechnet mithilfe der darzustellenden Generation die lebenden Zellen, 
    * die sich im Fenster befinden,  und speichert sie in einen OpenGl zulässigen Format.
    */
    
    int init_pointarr(int aa,int bb,int xuz,int yuz);
    
    
    /**
    * @brief initialisiert OpenGl
    *
    * Setzt fuer OpenGL Eigenschaften wie zB. die groesse der Anzeige, etc.
    */
    void init_opengl();
    
    /**
    * @brief Holt Zelle an Position x,y
    * @param x x-Koordinate der Zelle 
    * @param y y-Koordinate der Zelle 
    *
    * Fragt den Zustand der Zelle an Position x,y von der darzustellenden Generation ab.
    */   
    bool get_field(int x, int y); 
    
    /**
    * @brief Bufferswap
    * @param gldrawable Zeiger auf das Objekt(Fenster/DrawingArea), auf dem OpenGL zeichnen soll
    *
    * Tauscht den OpenGl Backbuffer gegen den Frontbuffer.
    */ 
    void openglswapbuffers(GdkGLDrawable *gldrawable);
   
public:
 
 
/**
* @brief Erneuert das Feld, mit dem OpenGL die Generation anzeigt.
*
* Erneuert das Feld, mit dem OpenGL die Generation anzeigt, muss nach erfolgreichen Klicken im REC-Modus
* ausgeführt werden.
*/  
    

void renewPointarr();
   
/**
* @brief Aktualisiert die Dimension der darzustellenden Generation.
* @param f Zeiger auf das Feld, das sich geaendert hat
*
* Wenn sich die Dimension des Feldes aendert, aktuallisert sich diese mithilfe dieser Methode.
*/    
    void aktualizeFeld(Feld * f);
    

   
   
/**
* @brief Ermittlung der Zellen im Fenster und Neuzeichnen dieser auf der Drawing Area.
*
* Zeichnet die Drawing Area mittels OpenGl anhand der Daten in dem Parameter Feld neu.
* Berechnet dazu den Bildschirmausschnitt, der wirklich angezeigt werden muss, und eine
* eventuelle Verschiebung, die beim Scrollen eintritt, wenn der Zoom groesser eins ist 
* (so koennen am Rand "halbe" Rechtecke angezeigt werden).
* Eventuell wird der Mutex aufgehoben, auf dem der Aktionsmanager wartet, damit er die aktuelle Generation
* in die momentan dargestellte Generation kopieren kann, und die Gui wieder zum Zeichnen dieser Generation
* beauftragen kann.
*/  


    void showme();
    
    
/**
* @brief Neuzeichnen der Drawing Area.
*
* Zeichnet die Drawing Area mittels OpenGl anhand der Daten in dem Parameter Feld neu.
* Berechnet jedoch das Pointarr-Feld nicht neu. Berechnet eine
* eventuelle Verschiebung, die beim Scrollen eintritt, wenn der Zoom groesser eins ist 
* (so koennen am Rand "halbe" Rechtecke angezeigt werden).
* Mutex wird nicht aufgehoben, da dieses Zeichnen nicht vom Aktionsmanager ausgefuehrt wurde, sondern durch Benutzer veranlasst.
*/  


    void exposeme();
   
/**
* @brief Zeichnet evtl ein Gitter und bei der Maus ein rotes Rechteck.
* @param mausx X-Position des Mauszeigers bezueglich des Fensters in Pixel
* @param mausy Y-Position des Mauszeigers bezueglich des Fensters in Pixel
*
* Berechnet die Zelle ueber der sich die Maus befindet, und stellt dort ein rotes Rechteck dar.
* Wenn der Zoom einen bestimmten Wert uebertreten hat, wird zur besseren Uebersicht ein Gitter angezeigt.
*/     
    void mausmove(int mausx, int mausy);
   
/**
* @brief Berechnet bei Mausklick die Position der Maus im Feld.
* @param mausx X-Position des Mauszeigers bezueglich des Fensters in Pixel
* @param mausy Y-Position des Mauszeigers bezueglich des Fensters in Pixel
*
* Befindet sich die Gui im Rec-modus und der Benutzer klickt auf das Fenster (welches das Feld darstellt)
* wird ueberprueft, ob der Klick auch innerhalb
* des Feldes stattfand, (falls das Feld kleiner als das Fenster ist) und gibt die Position der zu kippenden Zelle zurueck.
* 
*/       
    HeightWidth saveclick(int mausx, int mausy);

   
/**
* @brief Konstruktor
* @param window Pointer auf das window, benoetigt fuer Hoehe und Breite
* @param drawing_area OpenGL braucht diesen Pointer fuers Zeichnen
* @param adjxx Pointer auf die "Scrollbar" der Breite (keine Scrollbar, sondern das Objekt, was die Werte der Scrollbar enthaelt)
* @param adjyy Pointer auf die "Scrollbar" der Hoehe
*
* Erstellt den Viewer, und speichert die ganzen übergebenen Parameter, da es diese staendig benoetigt. 
* Erzeugt das Feld Pointarr, mittels dem OpenGL auf der DrawingArea zeichnet. 
* Groesse von Pointarr ist maximale Bildschirmaufloessung * 4 (da fuer jede Zelle ein Quadrat gezeichnet wird).  
* 
*/
    Viewer(Gtk::Window *window,GtkWidget *  drawing_area,Gtk::Adjustment *adjxx,Gtk::Adjustment *adjyy,Gtk::Adjustment *adjzz, int maxxdim, int maxydim);//, Glib::Dispatcher *mm_dispatcher);

   
   
   
    };



#endif
